/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/


/**
 * 1. reject和resolve是用来更改Promise的状态的  状态一旦确认就不能被改变
 * 2. then方法对状态做出判断 如果成功就执行成功回调 否则执行失败回调 同时传入执行结果 Promise要兼容同步和异步处理
 * 3. then方法实现多次调用 添加多个处理函数
 * 4. then方法要实现链式调用 前一个then方法返回的值要作为第二个then方法回调函数的参数 这个值可以是一个基本类型的值也可以是一个新的Promise对象 但不可以是当前promise本身
 * 5. then方法异步链式调用以及捕获错误
 * 6. then方法参数变为可选参数
 * Promise的静态方法
 * 7. Promise.all()
 * 8. Promise.resolve()
 * 9. .finally()方法实现 ：接收一个回调参数 当自身状态确定后 返回Promise1对象  同时回调里可返回一个Promise2对象 等待这个Promise2执行完成之后再去Promise1执行返回结果
*/
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
    constructor(exector){
        try{
            exector(this.resolve, this.reject)
        }catch (e){
            this.reject(e)
        }
    }
    status = PENDING
    success = undefined
    reason = undefined
    successCallBack = []
    failCallBack = []
    resolve = value =>{
        if(this.status !== PENDING) return
        this.status = FULFILLED
        this.success = value
        while(this.successCallBack.length) this.successCallBack.shift()()
    }
    reject = reason =>{
        if(this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        while(this.failCallBack.length) this.failCallBack.shift()()
    }
    then(successCallBack, failCallBack){
        successCallBack = successCallBack ? successCallBack : value => value
        failCallBack = failCallBack ? failCallBack : reason => { throw reason }
        let promise2 = new MyPromise((resolve, reject) =>{
            if(this.status == FULFILLED){
                setTimeout(()=>{
                    try{
                        let x = successCallBack(this.success)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch (e){
                        reject(e)
                    }
                },0)
            }else if(this.status == REJECTED){
                setTimeout(()=>{
                    try{
                        let x = failCallBack(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch (e){
                        reject(e)
                    }
                },0)
            }else{
                //PEDIND状态
                this.successCallBack.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = successCallBack(this.success)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch (e){
                            reject(e)
                        }
                    },0)
                })
                this.failCallBack.push(() =>{
                    setTimeout(()=>{
                        try{
                            let x = failCallBack(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch (e){
                            reject(e)
                        }
                    },0)
                })
            }
        })
        return promise2
        
    }
    finally(callBack){
        return this.then(value =>{
            let x = callBack() //视频教学中不区分callBack的返回 当callback没有返回时 无法正确返回
            // return MyPromise.resolve(callBack()).then(()=> value) 
            if(x instanceof MyPromise) return MyPromise.resolve(x).then(()=> value) //不传入.then()的第二个回调函数 这样callBack里面的Promise执行失败时 会返回其失败原因 而不会返回value
            return value
        }, reason =>{
            let x = callBack()
            if(x instanceof MyPromise) return MyPromise.resolve(x).then(()=> reason) //不传入.then()的第二个回调函数 这样callBack里面的Promise执行失败时 会返回其失败原因 而不会返回value
            return reason
        })
    }
    catch(failCallBack){
        return this.then(undefined,failCallBack)
    }
    static all(array){
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addArr(key, value){
                index ++
                result[key] = value
                if(array.length === index) resolve(result)
            }
            for(let i in array){
                let current = array[i]
                if(current instanceof MyPromise){
                    current.then(value => addArr(i, value), reason => reject(reason))
                }else addArr(i, current)
            }
        })
    }
    static resolve(value){
        if(value instanceof MyPromise) return value
        return new Promise(resolve => resolve(value))
    }
}
function resolvePromise (promise, x, resolve, reject){
    if(promise === x) { //这里有个难点无法理解 外面链式调用时x与promise是不相等的 使用定义的变量调用.then时是相等的
       return reject(new TypeError('cannot return himself'))
    }
    if(x instanceof MyPromise){
        x.then(resolve, reject)
    }else {
        resolve(x)
    }
}
module.exports = MyPromise